﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Web.Script.Serialization;

class Config
{
    public List<string> FileList { get; set; }
    public int TimeOut { get; set; } = 1000;
}


namespace ProcessMonitor
{
    class Program
    {
        public static bool isConsole = true;

        static void Main(string[] args)
        {
            bool watch = false;
            string paths = null;
            string flag = null;
            double timeOut=1;
            int delay = 0;
            string version= "v" + Assembly.GetExecutingAssembly().GetName().Version;
            try
            {
                Console.Title = Assembly.GetExecutingAssembly().GetName().Name + " " + version;
            }
            catch (Exception)
            {
                isConsole = false;
            }

            MyLog.WriteLine("Process loader " + version);

            if (args.Length == 0)
            {
                MyLog.WriteLine("");
                MyLog.WriteLine("使用方法：");
                MyLog.WriteLine("loader paths=value [watch=true|false] [timeout=0.5-60] [delay=1-300]");
                MyLog.WriteLine("例1：loader paths=\"C:\\abc.exe\"");
                MyLog.WriteLine("例2：loader paths=\"C:\\abc.exe\" watch=true");
                MyLog.WriteLine("例3：loader paths=\"C:\\abc.exe\" delay=10");
                MyLog.WriteLine("例4：loader paths=\"C:\\abc.exe\" watch=true timeout=1 delay=10");
                MyLog.WriteLine("");
                MyLog.WriteLine("参数说明：");
                MyLog.WriteLine("paths：要监视的程序路径，使用双引号包含，可以有多个程序，多个之间使用英文逗号分隔，不写完整路径则为当前路径");
                MyLog.WriteLine("watch：是否监视进程，默认否，取值true和false");
                MyLog.WriteLine("timeout：守护间隔，默认1秒，取值0.5-60秒，watch为false时无意义");
                MyLog.WriteLine("delay：程序启动延时，默认0，取值1-300秒");
                MyLog.WriteLine("");
                MyLog.WriteLine("10秒后退出...");
                Thread.Sleep(10 * 1000);
                return;
            }
            
            //if (args.Length > 0)
            //{

            //    //Thread t= new Thread(new ThreadStart(() => WatchDog(args[0])));
            //    //t.Start();
            //    //t.Join();
            //    //WatchDog(args[0]);
            //    string json = args[0].Replace("\\","\\\\").Replace("\"","\\\"");
            //    //反系列json到数组
            //    Debug.Print(json);
            //    JavaScriptSerializer serializer = new JavaScriptSerializer();
            //    Config cfgJson = serializer.Deserialize<Config>(args[0]);
            //}

            foreach (var arg in args)
            {
                flag = "watch";
                if (arg.StartsWith(flag + "="))
                {
                    watch = Convert.ToBoolean(Data.MatchData(arg, @"(?<=" + flag + "=).*").FirstOrDefault());
                }

                flag = "paths";
                if (arg.StartsWith(flag + "="))
                {
                    paths= Data.MatchData(arg, @"(?<=" + flag + "=).*").FirstOrDefault();
                }

                flag = "timeout";
                if (arg.StartsWith(flag + "="))
                {
                    timeOut = double.Parse(Data.MatchData(arg, @"(?<=" + flag + "=).*").FirstOrDefault());
                    if (timeOut < 0.5) timeOut = 0.5;
                    if (timeOut > 60) timeOut = 60;
                    timeOut = Math.Round(timeOut, 1);
                }
                flag = "delay";
                if (arg.StartsWith(flag + "="))
                {
                    delay = int.Parse(Data.MatchData(arg, @"(?<=" + flag + "=).*").FirstOrDefault());
                    if (delay < 0) timeOut = 0;
                    if (delay > 300) timeOut = 300;
                }
            }
            //Thread t = new Thread(new ThreadStart(() => LoadPro(paths,watch, delay, timeOut)));
            //t.Start();
            //t.Join();
            MyLog.WriteLine(0,"Watch: " + (watch == true?"yes":"no") + "\tWatch interval: " + timeOut + "s\tDelay: " + delay + "s");
            if (string.IsNullOrEmpty(paths))
            {
                MyLog.WriteLine(2, "The program to watch is empty!");
                MyLog.WriteLine(0, "Exit in 5 seconds...");
                Thread.Sleep(5000);
            }
            else
            {
                LoadPro(paths, watch, delay, timeOut);
            }  
        }

        static void LoadPro(string paths, bool watch , int delay,double timeOut)
        {
            bool isShow = true;
            MyLog.WriteLine(0, "Processes to watch: " + paths);
            if (delay>0)
            {
                MyLog.WriteLine(0, "The watchdog will automatically start after " + delay + " seconds...");
                                MyLog.WriteLine(0, "Start in " + delay + " seconds...");

                for (int i = 0; i < delay; i++)
                {
                    if (isConsole)
                        MyLog.WriteLine(0, "Start in " + (delay - i).ToString() + " seconds...", 0, Console.CursorTop - 1);
                    Thread.Sleep(1000);
                }
                if (isConsole) 
                    Console.SetCursorPosition(0, Console.CursorTop - 1);
            }
            else
            {
                MyLog.WriteLine(0, "The watchdog is running...");
            }
            string[] pathArray = paths.Split(',');
            List<FileInfo> fiList = new List<FileInfo>();
            foreach (string path in pathArray)
            {
                if (File.Exists(path))
                {
                    fiList.Add(new FileInfo(path));
                }
            }
            do
            {
                foreach (FileInfo fi in fiList)
                {
                    if (!fi.Exists)
                    {
                        MyLog.WriteLine(2, "File " + fi.Name + " not exist!");
                        Console.ReadKey();
                        return;
                    }
                    int pid = Shell.ProcessExist(fi.Name);
                    if (pid > 0)
                    {
                        if (isShow)
                        {
                            MyLog.WriteLine(0, "The process exist, PID: " + pid);
                            isShow = false;
                        }
                    }
                    else
                    {
                        MyLog.WriteLine(1, "Process not exist!");
                        MyLog.WriteLine(0, "Start the process...");
                        Shell.RunFile(null, fi.FullName, null, fi.DirectoryName, ProcessWindowStyle.Normal);
                        isShow = true;
                    }
                }
                Thread.Sleep((int)timeOut * 1000);
            } while (watch);
            MyLog.WriteLine(0, "The program is running and is about to exit...");
            Thread.Sleep(2000);
            //Console.ReadKey();
        }

	}


    class Data
    {
        public static bool IsMatch(string input, string pattern)
        {
            Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
            Match match = r.Match(input);
            if (match.Success)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public static List<string> MatchData(string input, string pattern)
        {
            Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
            List<string> rList = new List<string>();
            MatchCollection matchs = r.Matches(input);
            foreach (Match item in matchs)
            {
                if (!string.IsNullOrEmpty(item.Value))
                {
                    rList.Add(item.Value);
                }
            }
            return rList;
        }
    }


    class Shell
    {
        public const int RUN_CMD = 0;
        public const int RUN_FILE = 1;
        public delegate bool CallBack(string input, string args = null);

        public static int ProcessExist(string fileName)
        {
            FileInfo fi = new FileInfo(fileName);
            string proName = Data.MatchData(fi.Name, @".*(?=\.)").FirstOrDefault();
            Process[] pro = Process.GetProcessesByName(proName);
            if (pro.Length>0)
            {
                return pro[0].Id;
            }
            else
            {
                return 0;
            }
        }
        //运行命令
        public static int RunCmd(CallBack lpProcFunc, string cmdLine, string args = null, string workingPath = null,
            ProcessWindowStyle WindowStyle = ProcessWindowStyle.Hidden, bool standardOutput = true, bool onlineOutput = true, bool waitExit = false)
        {
            int RUN_CMD = 0;
            if (cmdLine != null)
            {
                int result = Run(lpProcFunc, RUN_CMD, cmdLine, args, workingPath, WindowStyle, standardOutput, onlineOutput, waitExit);
                return result;
            }
            else
            {
                return 0;
            }
        }

        //运行文件
        public static int RunFile(CallBack lpProcFunc, string fileFullName, string args = null, string workingPath = null,
            ProcessWindowStyle WindowStyle = ProcessWindowStyle.Hidden, bool delFlag = false, bool standardOutput = false, bool onlineOutput = false, bool waitExit = false)
        {
            int RUN_FILE = 1;
            //List<string> result = null;
            FileInfo fiInfo = new FileInfo(fileFullName);
            if (fiInfo.Exists)
            {
                int result = Run(lpProcFunc, RUN_FILE, fiInfo.FullName, args, workingPath, WindowStyle, standardOutput, onlineOutput, waitExit);
                fiInfo.Refresh();
                if (delFlag && fiInfo.Exists)
                {
                    try
                    {
                        fiInfo.Delete();
                        fiInfo.Refresh();
                    }
                    catch (Exception e)
                    {
                        Debug.Print(MyLog.Format(2, e.Message));
                    }
                }
                return result;
            }
            else
            {
                return 0;
            }
        }

        //运行过程函数
        private static int Run(CallBack ProcFunc, int runType, string inputValue, string args, string workingPath,
            ProcessWindowStyle WindowStyle, bool standardOutput, bool onlineOutput, bool waitExit)
        {
            inputValue = inputValue ?? string.Empty;
            args = args ?? string.Empty;
            workingPath = workingPath ?? Environment.CurrentDirectory;
            //List<string> resultList = new List<string>();
            int PID = 0;
            using (Process cmdProcess = new Process())
            {
                switch (runType)
                {
                    case 0:
                        cmdProcess.StartInfo.FileName = Environment.GetEnvironmentVariable("ComSpec");
                        cmdProcess.StartInfo.Arguments = "/C " + inputValue + " " + args;
                        break;
                    case 1:
                        cmdProcess.StartInfo.FileName = inputValue;
                        cmdProcess.StartInfo.Arguments = args;
                        break;
                    default:
                        break;
                }
                //设置工作目录
                cmdProcess.StartInfo.WorkingDirectory = workingPath;
                if (standardOutput)
                {
                    //关闭Shell的使⽤
                    cmdProcess.StartInfo.UseShellExecute = false;
                    //设置窗口编码
                    //process.StartInfo.StandardOutputEncoding = Encoding.Default;
                    //重新定向标准输⼊，输⼊，错误输出
                    cmdProcess.StartInfo.RedirectStandardInput = true;
                    cmdProcess.StartInfo.RedirectStandardOutput = true;
                    cmdProcess.StartInfo.RedirectStandardError = true;
                }
                //不创建窗口
                cmdProcess.StartInfo.CreateNoWindow = true;
                //设置调用程序显示状态，true显示
                cmdProcess.StartInfo.WindowStyle = WindowStyle;
                try
                {
                    //开始
                    if (!cmdProcess.Start()) return 0;
                    PID = cmdProcess.Id;
                    Debug.Print(MyLog.Format(0, "Current process pid " + PID));
                    //直接运行方法，最后需要加exit
                    //process.StandardInput.WriteLine("ping 192.168.31.1");
                    //cmdProcess.StandardInput.WriteLine("exit");

                    bool isProceLoop = true;
                    //获取结果
                    if (standardOutput)
                    {
                        if (onlineOutput)
                        {
                            MyLog.WriteLog(0, "Start output ReadLine...");
                            while (!cmdProcess.StandardOutput.EndOfStream)
                            {
                                string outLine = cmdProcess.StandardOutput.ReadLine();
                                MyLog.WriteLog(0, outLine);
                                if (isProceLoop)
                                {
                                    if (ProcFunc != null)
                                    {
                                        if (ProcFunc(outLine, PID.ToString()))
                                        {
                                            isProceLoop = false;
                                            return PID;
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            MyLog.WriteLog(0, "Wait output ReadToEnd...");
                            string result = cmdProcess.StandardOutput.ReadToEnd();
                            string[] results = result.Replace("\r\n", "\n").Replace("\t", "").Split('\n');
                            //ProcFunc?.Invoke(item);       //该方法可以直接判断函数是否为null，不为null运行
                            foreach (string outLine in results)
                            {
                                MyLog.WriteLog(0, "Output: " + outLine);
                                if (isProceLoop)
                                {
                                    if (ProcFunc(outLine, PID.ToString()))
                                    {
                                        isProceLoop = false;
                                        return PID;
                                    }
                                }
                            }
                            //resultList.AddRange(results);
                        }
                    }
                    if (waitExit)
                    {
                        MyLog.WriteLog(0, "Waiting for the process to exit...");
                        cmdProcess.WaitForExit();
                    }
                }
                catch (Exception e)
                {
                    MyLog.WriteLog( 2, e.ToString());
                }
            }
            //等待按任意键关闭
            //Console.ReadKey();
            return PID;
        }


    }


    class MyLog
    {
        private static string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
        private static FileInfo fiLog = new FileInfo(assemblyName + ".log");
        public static string Format(int ClassInt, string logText, bool OutputTime = true)
        {
            string logDate = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
            string result;
            if (OutputTime)
            {
                result = string.Format("{0} [{1}] {2}", logDate, GetClass(ClassInt), logText);
            }
            else
            {
                result = string.Format("[{0}] {1}", GetClass(ClassInt), logText);
            }
            return result;
        }

        public static string GetClass(int ClassValue)
        {
            switch (ClassValue)
            {
                case 0:
                    return "INFO";
                //break;
                case 1:
                    return "WARN";
                //break;
                case 2:
                    return "ERROR";
                //break;
                default:
                    return "NULL";
                    //break;
            }
        }

        public static void WriteLine(string input=null,int left=0,int top=0)
        {
            if (left >0 || top>0)
            {
                Console.SetCursorPosition(left, top);
            }
            Console.WriteLine(input);
        }

        public static void WriteLine(int classValue ,string input=null,int left=0,int top=0)
        {
            if (left > 0 || top > 0)
            {
                Console.SetCursorPosition(left, top);
            }
            Console.WriteLine(Format(classValue,input));
            //Debug.Print(Format(classValue, input));
        }
        public static void Write(string input,int left=0,int top=0)
        {
            if (left > 0 || top > 0)
            {
                Console.SetCursorPosition(left, top);
            }
            Console.Write(input);
            //Debug.Print(input);
        }


        public static void WriteLog(int classValue, string input)
        {
            try
            {
                WriteLine(0,input);
                using (StreamWriter sw = fiLog.AppendText())
                {
                    sw.WriteLine(Format(classValue, input));
                }
            }
            catch (Exception e)
            {
                Debug.Print(Format(2, e.ToString()));
            }
        }

        public static void WriteLog(FileInfo fi,int classValue, string input)
        {
            try
            {
                WriteLine(0,input);
                using (StreamWriter sw = fi.AppendText())
                {
                    sw.WriteLine(Format(classValue,input));
                }
            }
            catch (Exception e)
            {
                Debug.Print(Format(2, e.ToString()));
            }
        }
    }



}


